package com.imooc.mall.service.impl;

import com.google.gson.Gson;
import com.imooc.mall.common.RedisKeyConst;
import com.imooc.mall.common.ResultInfo;
import com.imooc.mall.common.ResultInfoUtil;
import com.imooc.mall.dto.CartDto;
import com.imooc.mall.dto.CartProductDto;
import com.imooc.mall.dto.CartUpdateDto;
import com.imooc.mall.exception.MallException;
import com.imooc.mall.form.CartAddForm;
import com.imooc.mall.pojo.CartForRedis;
import com.imooc.mall.pojo.Product;
import com.imooc.mall.service.ICartService;
import com.imooc.mall.service.IProductService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.IntSummaryStatistics;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.imooc.mall.enums.ProductStatusEnum.ON_SALE;

@Service
@Slf4j
public class CartServiceImpl implements ICartService {
    /**
     * 添加商品到购物车
     * 1.商品是否存在
     * 2.商品状态是否在售
     * 3.商品库存是否足够
     *
     * @param cartAddForm
     * @return
     */
    @Autowired
    private IProductService productService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public ResultInfo<CartDto> add(CartAddForm cartAddForm, Integer uid) {
        log.info("添加购物车==>用户：{}，商品：{}",uid,cartAddForm.getProductId());
        //商品是否存在
        Product product = productService.findById(cartAddForm.getProductId());
        if (null == product) {
            throw new MallException("商品不存在");
        }
        //商品状态是否在售
        if (!product.getStatus().equals(ON_SALE.getCode())) {
            throw new MallException("商品下架或已删除");
        }
        if (product.getStock() <= 0) {
            throw new MallException("库存不足");
        }
        //写入redis
        //商品id,数量，是否选中写入redis，其他的如经常变动的字段从数据库获取，保证能实时
      /*  redisTemplate.opsForValue().set(String.format(RedisKeyConst.CART_REDIS_KEY,uid),
                new Gson().toJson(new CartForRedis(product.getId(),1,cartAddForm.getSelected())));
            */
        CartForRedis cartForRedis;
        String redisKey = String.format(RedisKeyConst.CART_REDIS_KEY, uid);
        String productKey = product.getId() + "";

        HashOperations<String, String, String> hashOperation = redisTemplate.opsForHash();
        //写之前需要判断redis是否有数据
        String value = hashOperation.get(redisKey, productKey);
        if (StringUtils.isBlank(value)) {
            //没有该商品，新增
            cartForRedis = new CartForRedis(product.getId(), 1, cartAddForm.getSelected());
        } else {
            //有，该商品数量需要加1
            cartForRedis = new Gson().fromJson(value, CartForRedis.class);
            cartForRedis.setQuantity(cartForRedis.getQuantity() + 1);
        }
        hashOperation.put(redisKey, productKey,
                new Gson().toJson(cartForRedis));

        return list(uid);
    }

    @Override
    public ResultInfo list(Integer uid) {
        CartDto cartDto = new CartDto();
        List<CartProductDto> list = new ArrayList<>();
        Boolean selectAll=true;//是否全选
        Integer totalQuantity=0;//购物车总数量
        BigDecimal totalPrice= BigDecimal.valueOf(0);//购物车总金额
        String redisKey = String.format(RedisKeyConst.CART_REDIS_KEY, uid);
        HashOperations<String, String, String> hashOperation =
                redisTemplate.opsForHash();
        Map<String, String> entries = hashOperation.entries(redisKey);
        for (Map.Entry<String, String> entry : entries.entrySet()) {
            CartForRedis cartForRedis = new Gson().fromJson(entry.getValue(), CartForRedis.class);
            //todo 需要优化 使用mysql里in
            Integer productId = Integer.valueOf(entry.getKey());
            Product product = productService.findById(productId);
            if (null != product) {
                CartProductDto cartProductDto =
                        new CartProductDto(productId,
                                cartForRedis.getQuantity(),
                                product.getName(),
                                product.getSubtitle(),
                                product.getMainImage(),
                                product.getPrice(),
                                product.getStatus(),
                                product.getPrice().multiply(BigDecimal.valueOf(cartForRedis.getQuantity())),
                                product.getStock(),
                                cartForRedis.getProductSelected()
                        );
                list.add(cartProductDto);
                if (!cartForRedis.getProductSelected()){
                    selectAll=false;//如果有一个没选，就不叫全选
                }
                //bigDecimal计算累加方式需要注意！
                //且选中的商品才做累加
                if (cartForRedis.getProductSelected()){

                    totalPrice = totalPrice.add(cartProductDto.getProductTotalPrice());
                }
            }
            //总数来自redis
            totalQuantity+=cartForRedis.getQuantity();

        }

        cartDto.setSelectedAll(selectAll);
        cartDto.setCartTotalQuantity(totalQuantity);
        cartDto.setCartTotalPrice(totalPrice);
        cartDto.setCartProductDtoList(list);

        return ResultInfoUtil.success(cartDto);
    }

    @Override
    public ResultInfo<CartDto> update(Integer uid, Integer productId, CartUpdateDto cartUpdateDto) {
        String redisKey = String.format(RedisKeyConst.CART_REDIS_KEY, uid);
        HashOperations<String, String, String> hashOperation =
                redisTemplate.opsForHash();
        String value = hashOperation.get(redisKey, String.valueOf(productId));
        if (StringUtils.isBlank(value)) {
            log.error("无此商品==>>{}",productId);
            throw new MallException("无此商品");
        }
            //存在，则可以修改
            CartForRedis cartForRedis = new Gson().fromJson(value, CartForRedis.class);
            if (cartUpdateDto.getQuantity()!=null && cartUpdateDto.getQuantity()>=0){
                cartForRedis.setQuantity(cartUpdateDto.getQuantity());
            }
            //如果传了是否选中
            if (cartUpdateDto.getIsSelected()!=null){
                cartForRedis.setProductSelected(cartUpdateDto.getIsSelected());
            }
            //装载
            hashOperation.put(redisKey,String.valueOf(productId),
                    new Gson().toJson(cartForRedis));

        return list(uid);
    }

    @Override
    public ResultInfo<CartDto> delete(Integer uid, Integer productId) {

        String redisKey = String.format(RedisKeyConst.CART_REDIS_KEY, uid);
        HashOperations<String, String, String> hashOperation =
                redisTemplate.opsForHash();
        String value = hashOperation.get(redisKey, String.valueOf(productId));
        if (StringUtils.isBlank(value)) {
            log.error("无此商品==>>{}",productId);
            throw new MallException("无此商品");
        }
        hashOperation.delete(redisKey,productId+"");
        return list(uid);
    }

    @Override
    public ResultInfo<CartDto> selectAll(Integer uid) {
        String redisKey = String.format(RedisKeyConst.CART_REDIS_KEY, uid);
        HashOperations<String, String, String> hashOperation =
                redisTemplate.opsForHash();
        //获取redis中的该用户下的所有购物车
        List<CartForRedis> cartListFromRedis = getCartListFromRedis(uid);
        //遍历，设置为选中
        List<CartForRedis> newCartList = cartListFromRedis.
                stream().
                map(x -> getY(x,true)).collect(Collectors.toList());
        //将新的newCartList加入redis
        //redisTemplate.opsForHash().put();
        for (CartForRedis cartForRedis : newCartList) {
            hashOperation.put(redisKey,
                    cartForRedis.getProductId()+"",
                    new Gson().toJson(cartForRedis));
        }
        return list(uid);
    }

    private CartForRedis getY(CartForRedis x,Boolean flag) {
        CartForRedis y=new CartForRedis();
        x.setProductSelected(flag);
        BeanUtils.copyProperties(x,y);
        return y;
    }


    public List<CartForRedis> getCartListFromRedis(Integer uid) {
        String redisKey = String.format(RedisKeyConst.CART_REDIS_KEY, uid);
        HashOperations<String, String, String> hashOperation =
                redisTemplate.opsForHash();
        Map<String, String> entries = hashOperation.entries(redisKey);
        List<CartForRedis> cartForRedisList = entries.entrySet().
                stream().
                map(x -> new Gson().fromJson(x.getValue(),
                        CartForRedis.class)).
                collect(Collectors.toList());
        return cartForRedisList;
    }

    @Override
    public ResultInfo<CartDto> unSelectAll(Integer uid) {
        String redisKey = String.format(RedisKeyConst.CART_REDIS_KEY, uid);
        HashOperations<String, String, String> hashOperation =
                redisTemplate.opsForHash();
        //获取redis中的该用户下的所有购物车
        List<CartForRedis> cartListFromRedis = getCartListFromRedis(uid);
        List<CartForRedis> newCollect = cartListFromRedis.stream().map(x -> getY(x, false)).collect(Collectors.toList());
        for (CartForRedis cartForRedis : newCollect) {
            hashOperation.put(redisKey,
                    cartForRedis.getProductId()+"",
                    new Gson().toJson(cartForRedis));
        }
        return list(uid);

    }

    @Override
    public ResultInfo<Integer> sum(Integer uid) {
        //获取redis中的该用户下的所有购物车
        List<CartForRedis> cartListFromRedis = getCartListFromRedis(uid);
        //"data": {
        //    "count": 3,假如 CartForRedis::getProductId)三个id号 3
        //    "sum": 31, 假如 CartForRedis::getProductId)三个id号相加 26+27+28=81
        //    "min": 1, 假如 CartForRedis::getProductId) 三个id号  26号 27号 28号取最小 26
        //    "max": 28 假如 CartForRedis::getProductId) 三个id号  26号 27号 28号取最大 28
        //  }
        IntSummaryStatistics collect = cartListFromRedis.stream().collect(Collectors.summarizingInt(CartForRedis::getQuantity));

        return ResultInfoUtil.success(collect.getSum());
    }
}
